// copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
// see COPYRIGHT for reuse legalities
//

#import "RIBCommand.h"
#import "WWEveParser.h"  // for the WW_ defines...
#import "WWSceneClock.h"
#import "WW3DAttributeState.h"
#import <stdio.h>

@implementation RIBCommand

+ initialize { return [RIBCommand setVersion:1], self; }

- init
{
  [super init];

  n = 0;
  tokens = (RtToken *)NULL;
  parms = (RtPointer *)NULL;
  archiveVector = (char **)NULL;
  printfNVector = (int *)NULL;
  printfTypeVector = (int *)NULL;

  dirtyBoundingBox = TRUE;

  return self;
}


- awake
{
  [super awake];

  dirtyBoundingBox = TRUE;

  return self;
}


- (void)setN:(int)newN tokens:(RtToken *)newTokens parms:(RtPointer *)newParms archiveVector:(char **)newArchiveVector printfTypeVector:(int *)newPrintfTypeVector printfNVector:(int *)newPrintfNVector
{  
   int  i;

 
   if (n)
   {  for (i = 0; i < n; i++)
      {  // don't free the tokens; they're shared!
         free(parms[i]);
         free(archiveVector[i]);
         free(printfNVector);
         free(printfTypeVector);
      }
      if (newN > n)
      {  tokens = (RtToken *)NXZoneRealloc([self zone], tokens, (newN * sizeof(RtToken)));
         parms = (RtPointer *)NXZoneRealloc([self zone], parms, (newN * sizeof(RtPointer)));
         archiveVector = (char **)NXZoneRealloc([self zone], archiveVector, (newN * sizeof(archiveVector)));
         printfNVector = (int *)NXZoneRealloc([self zone], printfNVector, (newN * sizeof(int)));
         printfTypeVector = (int *)NXZoneRealloc([self zone], printfTypeVector, (newN * sizeof(int)));
      }
   }
   else
   {  tokens = (RtToken *)NXZoneMalloc([self zone], newN * sizeof(RtToken));
      parms = (RtPointer *)NXZoneMalloc([self zone], newN * sizeof(RtPointer));
      archiveVector = (char **)NXZoneMalloc([self zone], newN * sizeof(archiveVector));
      printfNVector = (int *)NXZoneMalloc([self zone], newN * sizeof(int));
      printfTypeVector = (int *)NXZoneMalloc([self zone], newN * sizeof(int));
   }
   n = newN;
   for (i = 0; i < n; i++)
   {  tokens[i] = newTokens[i];
      parms[i] = newParms[i];
      archiveVector[i] = newArchiveVector[i];
      printfNVector[i] = newPrintfNVector[i];
      printfTypeVector[i] = newPrintfTypeVector[i];
   }

   dirtyBoundingBox = TRUE;
   return ;
}

- free
{
  int i;


  //NXLogError("%s %d being free'ed\n", [[self class] name], self);
  // don't free the tokens; they're constants (i.e. RI_P, RI_PW, etc.)
  //NXLogError("RIBCommand %s: freeing %p", [[self class] name], self);
  for (i = 0; i < n; i++)
  {  if (parms[i]) { free(parms[i]); }
     if (archiveVector[i]) { free(archiveVector[i]); }
  }
  if (n)  
  {  if (printfNVector) { free(printfNVector); }
     if (printfTypeVector) { free(printfTypeVector); }
  }
  return [super free];
}

// this should only be called internally
// the receiving object needs to copy all the parameters using the passed in zone
// the assumption is that 
- _mallocPointersUsingZone:(NXZone *)zone
{
   if (n)
   {  tokens = (RtToken *)NXZoneMalloc([self zone], n * sizeof(RtToken));
      if (!tokens)
      {  return nil;
      }
      parms = (RtPointer *)NXZoneMalloc([self zone], n * sizeof(RtPointer));
      if (!parms)
      {  NXZoneFree(zone, tokens);
         return nil;
      }
      archiveVector = (char **)NXZoneMalloc([self zone], n * sizeof(char *));
      if (!archiveVector)
      {  NXZoneFree(zone, tokens);
         NXZoneFree(zone, parms);
         return nil;
      }
      printfNVector = (int *)NXZoneMalloc([self zone], n * sizeof(int));
      if (!printfNVector)
      {  NXZoneFree(zone, tokens);
         NXZoneFree(zone, parms);
         NXZoneFree(zone, archiveVector);
         return nil;
      }
      printfTypeVector = (int *)NXZoneMalloc([self zone], n * sizeof(int));
      if (!printfTypeVector)
      {  NXZoneFree(zone, tokens);
         NXZoneFree(zone, parms);
         NXZoneFree(zone, archiveVector);
         NXZoneFree(zone, printfNVector);
         return nil;
      }
   }
   else
   {  // n == 0
      tokens = (RtToken *)NULL;
      parms = (RtPointer *)NULL;
      archiveVector = (char **)NULL;
      printfNVector = (int *)NULL;
      printfTypeVector = (int *)NULL;
   }
   return self;
}

- _setToken:(char *)newToken parm:(RtPointer)newParm i:(int)i archiveInfo:(char *)archiveInfo type:(int)parmType count:(int)howMany usingZone:(NXZone *)zone
{
   int  j;


   tokens[i] = (RtToken)NXZoneMalloc(zone, (1 + strlen(newToken)));
   if (!tokens[i])
   {  return nil;
   }
   strcpy(tokens[i], newToken);

   archiveVector[i] = (char *)NXZoneMalloc(zone, (1 + strlen(archiveInfo)));
   if (!archiveVector[i])
   {  NXZoneFree(zone, tokens[i]);
      return nil;
   }
   strcpy(archiveVector[i], archiveInfo);

   printfTypeVector[i] = parmType;
   printfNVector[i] = howMany;

   // in a perfect world, I could just copy that chunk o memory over,
   // but I'm worried about alignment problems, so I'll do it the slow way...
   switch (printfTypeVector[i])
   {  case WW_FLOAT:  parms[i] = (RtFloat *)NXZoneMalloc(zone, sizeof(RtFloat) * howMany); 
                      for (j = 0; j < printfNVector[i]; j++)
		      {  *((RtFloat *)parms[i] + j) = *((RtFloat *)(newParm) + j);
		      }
                      break;
      case WW_COLOR:  parms[i] = (RtFloat *)NXZoneMalloc(zone, sizeof(RtFloat) * howMany); 
                      for (j = 0; j < printfNVector[i]; j++)
		      {  *((RtFloat *)parms[i] + j) = *((RtFloat *)(newParm) + j);
		      }
                      break;
      case WW_POINT:  parms[i] = (RtFloat *)NXZoneMalloc(zone, sizeof(RtFloat) * howMany); 
                      for (j = 0; j < printfNVector[i]; j++)
		      {  *((RtFloat *)parms[i] + j) = *((RtFloat *)(newParm) + j);
		      }
                      break;
      case WW_INT:    parms[i] = (int *)NXZoneMalloc(zone, sizeof(int) * howMany); 
                      for (j = 0; j < printfNVector[i]; j++)
		      {  *((int *)parms[i] + j) = *((int *)(newParm) + j);
		      }
                      break;
      // hmm, strings are weirder.  
      case WW_STRING: parms[i] = (char **)NXZoneMalloc(zone, sizeof(char *) * howMany); 
                      for (j = 0; j < printfNVector[i]; j++)
		      {  // need to additionally malloc up some memory here, folks
                         *((char **)parms[i] + j) = (char *)NXZoneMalloc(zone, (1 + strlen(*((char **)(newParm) + j))));
                         strcpy(*((char **)parms[i] + j), *((char **)(newParm) + j));
		      }
                      break;
   }	  
   return self;
}


- copyFromZone:(NXZone *)zone
{
  int  i;
  id   newCopy = [super copyFromZone:zone];

  // now we need to grovel over the parameters and copy them 
  // this is a pain, but boy, will it be cool...
  // okay, malloc up the base list of pointers
  [newCopy _mallocPointersUsingZone:zone];
  for (i = 0; i < n; i++)
  {  if (![newCopy _setToken:tokens[i] parm:parms[i] i:i 
                       archiveInfo:archiveVector[i] type:printfTypeVector[i] count:printfNVector[i] 
                       usingZone:zone])
     {  [newCopy free];
        return nil;
     }
  }
  
  return newCopy;
}

- _lerpParametersWith:b by:(float)u
{
  int        i, j;
  RtToken    *tokensB;
  char       **archiveVectorB;
  int        *printfNVectorB;
  int        *printfTypeVectorB;
  RtPointer  *parmsB;


  // okay, first thing to realize is that this instance already has its 
  // parameters set to the (u ==0) case here.  Therefore, when in doubt, 
  // you can leave it alone and you're fine.  We now want to grovel 
  // over the two objects' parameters.  
  if (n != [b n])
  { // hmm.  looks like trouble.  At some point, we'll have to bail,
    // since the parameter lists aren't the same length.  we could
    // bail right away, since we know that "self"'s parameters are set
    // to something reasonable...  Yea, let's bail.
    return self;
  }

  tokensB = [b tokens];
  archiveVectorB = [b archiveVector];
  printfNVectorB = [b printfNVector];
  printfTypeVectorB = [b printfTypeVector];
  parmsB = [b parms];

  // okay, at this point, we know that these two objects are the same 
  // class, and the length of their parameter lists are the same. Of
  // course, this might be just a lucky coincidence, and the actual 
  // parameters might be different types.  

  for (i = 0; i < n; i++)
  {  if ((tokens[i] == tokensB[i]) || (!strcmp(tokens[i], tokensB[i])))
     {  // great! they're the same token.
        // now we need to make sure they're the same type and length of data
        // if not, bail to the next parameter
        if ((printfNVector[i] == printfNVectorB[i]) && (printfTypeVector[i] == printfTypeVectorB[i]))
	{  // okay! we're golden. Let's do a lerp...
           switch (printfTypeVector[i])
           {  case WW_FLOAT:   for (j = 0; j < printfNVector[i]; j++)
		               {  *((RtFloat *)parms[i] + j) += ((*((RtFloat *)(parmsB[i]) + j) - *((RtFloat *)parms[i] + j)) * u);
		               }
                               break;
              case WW_COLOR:   for (j = 0; j < printfNVector[i]; j++)
		               {  *((RtFloat *)parms[i] + j) += ((*((RtFloat *)(parmsB[i]) + j) - *((RtFloat *)parms[i] + j)) * u);
		               }
                               break;
              case WW_POINT:   for (j = 0; j < printfNVector[i]; j++)
		               {  *((RtFloat *)parms[i] + j) += ((*((RtFloat *)(parmsB[i]) + j) - *((RtFloat *)parms[i] + j)) * u);
		               }
                               break;
              case WW_INT:     for (j = 0; j < printfNVector[i]; j++)
		               {  *((int *)parms[i] + j) += ((*((int *)(parmsB[i]) + j) - *((int *)parms[i] + j)) * u);
			       }
                               break;
              case WW_STRING:  // can't lerp strings without more info...
                               // maybe could encode a way to do it with enumerated values, but for now, bail...
                               break;
           }
        }
        else
        {  // okay, that parameter can't be lerp'ed; on to the next
        }
     }
     else
     {  // okay, that parameter can't be lerp'ed; on to the next
     }
  }

  

  return self;
}


// note: because we've made the WWSampleList "safe" for having
// multiple samples with the same data, it's perfectly valid to return
// yourself or b
- lerpWith:b by:(float)uValue
{
   id  newMe = nil;


   if (([self class] != [b class]) || (uValue <= 0.0))
   {  return self;
   }

   if (uValue >= 1.0)
   {  return b;
   }

   newMe = [self copyFromZone:[self zone]];

   // okay, now we have to get newMe to linearly interpolate it
   // parameters partway between "self" and "b"
   [newMe _lerpParametersWith:b by:uValue];

   return newMe;
}

- lerpSelfWith:b by:(float)uValue
{
   if (([self class] != [b class]) || (uValue <= 0.0))
   {  return self;
   }

   if (uValue >= 1.0)
   {  return b;
   }

   // okay, now we have to get newMe to linearly interpolate it
   // parameters partway between "self" and "b"
   [self _lerpParametersWith:b by:uValue];

   return self;
}

- (BOOL)pushesOrPopsCTM { return NO; }
- (BOOL)pushesCTM { return NO; }
- (BOOL)popsCTM { return NO; }

- (BOOL)hasBoundingBox { return NO; }

- (float)lastSampleIsAt { return 0.0; }

- (unsigned long int)maxSampleBandwidth  
{  
   unsigned long int maxSampleBandwidth = 0;
   int  i, j;

  
   if (!n)
   {  return maxSampleBandwidth;
   }
   for (i = 0; i < n; i++)
   {  maxSampleBandwidth += 1 + strlen(tokens[i]);
      switch (printfTypeVector[i])
      {  case WW_FLOAT:   maxSampleBandwidth += (printfNVector[i] * sizeof(RtFloat));
                          break;
         case WW_COLOR:   maxSampleBandwidth += (printfNVector[i] * sizeof(RtFloat));
                          break;
         case WW_POINT:   maxSampleBandwidth += (printfNVector[i] * sizeof(RtFloat));
                          break;
         case WW_INT:     maxSampleBandwidth += (printfNVector[i] * sizeof(long));
                          break;
         case WW_STRING:  for (j = 0; j < printfNVector[i]; j++)
 	                  {  maxSampleBandwidth += 1 + strlen(*((char **)(parms[i]) + j));
			  }
                          break;
      }	  
   }
   return maxSampleBandwidth;
}

- deriveHowManyPoints:(char *)archiveStr ofLength:(int)divisor
{
  char howManyStr[32];


  // the string looks like "[1234f]"
  strcpy(howManyStr, (archiveStr + 1));
  howManyStr[strlen(howManyStr) - 2] = '\0';
  howManyPoints = atoi(howManyStr) / divisor;

  return self;
}

- (BOOL)isMoot  {  return NO;  }
- (BOOL)isMootStartingAt:(float)startTime endingAt:(float)endTime  {  return [self isMoot];  }

// this routine, on the other hand, should be called by subclasses implementing -theSameAs:
- (BOOL)theSameAs:otherRIBCommand
{
  int  i, j;
  RtToken    *tokens2;
  RtPointer  *parms2;
  int        *printfNVector2;
  int        *printfTypeVector2;

  
  if ([self class] != [otherRIBCommand class])
  {  return NO;
  }

  if (n != [otherRIBCommand n])
  {  return NO;
  }

  tokens2 = [otherRIBCommand tokens];
  parms2 = [otherRIBCommand parms];
  printfNVector2 = [otherRIBCommand printfNVector];
  printfTypeVector2 = [otherRIBCommand printfTypeVector];

  // note that there is a false (but probably okay) assumption here
  // that the parameters are in the same order.  This doesn't have to be
  // true, but for my stuff, probably will be true.
  for (i = 0; i < n; i++)
  {  if (tokens[i] != tokens2[i])
     {  if (strcmp(tokens[i], tokens2[i]))
        {  return NO;
        }
     }

     switch (printfTypeVector[i])
     {  case WW_FLOAT:   if (printfNVector[i] != printfNVector2[i])
                         {  return NO;
                         }
                         for (j = 0; j < printfNVector[i]; j++)
	                 {  if (*((RtFloat *)(parms[i]) + j) != *((RtFloat *)(parms2[i]) + j))
			    return NO;
			 }
                         break;
        case WW_COLOR:   if (printfNVector[i] != printfNVector2[i])
                         {  return NO;
                         }
                         for (j = 0; j < printfNVector[i]; j++)
	                 {  if (*((RtFloat *)(parms[i]) + j) != *((RtFloat *)(parms2[i]) + j))
			    return NO;
			 }
                         break;
        case WW_POINT:   if (printfNVector[i] != printfNVector2[i])
                         {  return NO;
                         }
                         for (j = 0; j < printfNVector[i]; j++)
	                 {  if (*((RtFloat *)(parms[i]) + j) != *((RtFloat *)(parms2[i]) + j))
			    return NO;
			 }
                         break;
        case WW_INT:   if (printfNVector[i] != printfNVector2[i])
                         {  return NO;
                         }
                         for (j = 0; j < printfNVector[i]; j++)
	                 {  if (*((RtInt *)(parms[i]) + j) != *((RtInt *)(parms2[i]) + j))
			    return NO;
			 }
                         break;
        case WW_STRING:   if (printfNVector[i] != printfNVector2[i])
                         {  return NO;
                         }
                         for (j = 0; j < printfNVector[i]; j++)
	                 {  if (strcmp(*((char **)(parms[i]) + j), *((char **)(parms2[i]) + j)))
			    return NO;
			 }
                         break;
     }	  
  }
  return YES;
}



- (BOOL)similarTo:otherRIBCommand 
{
  if ([self class] != [otherRIBCommand class])
  {  return NO;
  }
  if (n != [otherRIBCommand n])
  {  return NO;
  }
  return YES;
}


- calculateBoundingBoxStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  int      i = 0, j = 1;
  BOOL     notFound = YES;
  RtFloat  *aPointPtr;


  while (notFound && (i < n))
  {  if (!strcmp(tokens[i], "P"))
     {  notFound = NO;
        aPointPtr = (RtFloat *)(parms[i]);
	boundingBox[0] = aPointPtr[0];
	boundingBox[1] = aPointPtr[0];
	boundingBox[2] = aPointPtr[1];
	boundingBox[3] = aPointPtr[1];
	boundingBox[4] = aPointPtr[2];
	boundingBox[5] = aPointPtr[2];
        if (!howManyPoints)
	{  [self deriveHowManyPoints:archiveVector[i] ofLength:3];
        }
        while (j++ < howManyPoints)
        {  if (aPointPtr[0] < boundingBox[0]) { boundingBox[0] = aPointPtr[0]; }
           if (aPointPtr[0] > boundingBox[1]) { boundingBox[1] = aPointPtr[0]; }
           if (aPointPtr[1] < boundingBox[2]) { boundingBox[2] = aPointPtr[1]; }
           if (aPointPtr[1] > boundingBox[3]) { boundingBox[3] = aPointPtr[1]; }
           if (aPointPtr[2] < boundingBox[4]) { boundingBox[4] = aPointPtr[2]; }
           if (aPointPtr[2] > boundingBox[5]) { boundingBox[5] = aPointPtr[2]; }
           aPointPtr += 3;
        }
     }
     if (!strcmp(tokens[i], "Pz"))
     {  notFound = NO;
        aPointPtr = (RtFloat *)(parms[i]);
	boundingBox[0] = 0.0;
	boundingBox[1] = 1.0;
	boundingBox[2] = 0.0;
	boundingBox[3] = 1.0;
	boundingBox[4] = aPointPtr[0];
	boundingBox[5] = aPointPtr[0];
        if (!howManyPoints)
	{  [self deriveHowManyPoints:archiveVector[i] ofLength:1];
        }
        while (j++ < howManyPoints)
        {  if (aPointPtr[0] < boundingBox[4]) { boundingBox[4] = aPointPtr[0]; }
           if (aPointPtr[0] > boundingBox[5]) { boundingBox[5] = aPointPtr[0]; }
           aPointPtr++;
        }
     }
     if (!strcmp(tokens[i], "Pw"))
     {  notFound = NO;
        aPointPtr = (RtFloat *)(parms[i]);
	boundingBox[0] = ((aPointPtr[0]) / (aPointPtr[3]));
	boundingBox[1] = ((aPointPtr[1]) / (aPointPtr[3]));
	boundingBox[2] = ((aPointPtr[2]) / (aPointPtr[3]));
	boundingBox[3] = ((aPointPtr[0]) / (aPointPtr[3]));
	boundingBox[4] = ((aPointPtr[1]) / (aPointPtr[3]));
	boundingBox[5] = ((aPointPtr[2]) / (aPointPtr[3]));
        if (!howManyPoints)
	{  [self deriveHowManyPoints:archiveVector[i] ofLength:4];
        }
        while (j++ < howManyPoints)
        {  if ((aPointPtr[0]/aPointPtr[3]) < boundingBox[0])
           { boundingBox[0] = (aPointPtr[0]/aPointPtr[3]); 
           }
           if ((aPointPtr[0]/aPointPtr[3]) > boundingBox[1])
           { boundingBox[1] = (aPointPtr[0]/aPointPtr[3]); 
           }
           if ((aPointPtr[1]/aPointPtr[3]) < boundingBox[2])
           { boundingBox[2] = (aPointPtr[1]/aPointPtr[3]); 
           }
           if ((aPointPtr[1]/aPointPtr[3]) > boundingBox[3])
           { boundingBox[3] = (aPointPtr[1]/aPointPtr[3]); 
           }
           if ((aPointPtr[2]/aPointPtr[3]) < boundingBox[4])
           { boundingBox[4] = (aPointPtr[2]/aPointPtr[3]); 
           }
           if ((aPointPtr[2]/aPointPtr[3]) > boundingBox[5])
           { boundingBox[5] = (aPointPtr[2]/aPointPtr[3]); 
           }
           aPointPtr += 4;
        }
     }
     i++;
  }

  if (notFound)
  {  NXLogError("unable to find any points token (RI_P, RI_Z, RI_PW) for %s - can't calculate bounding box...", [[self class] name]);
     return nil;
  }
  dirtyBoundingBox = FALSE; 
  return self;
}


- setBoundingBox:(RtBound *)newBoundingBox
{
  N3D_CopyBound(*newBoundingBox, boundingBox);

  return self;
}

- getBoundingBox:(RtBound *)boundingBoxContainer
{
  N3D_CopyBound(boundingBox, *boundingBoxContainer);

  return self;
}

- (RtBound *)boundingBoxStartingAt:(RtFloat)intervalStartTime endingAt:(RtFloat)intervalEndTime
{ 
   if (dirtyBoundingBox) 
   {  [self calculateBoundingBoxStartingAt:intervalStartTime endingAt:intervalEndTime];
   }
   return &boundingBox; 
}

- setMyShape:shape { myShape = shape;  return self; }

- shape { return myShape; }

- (int)n { return n; }
- (RtToken *)tokens { return tokens; }
- (RtPointer *)parms { return parms; }
- (char **)archiveVector { return archiveVector; }
- (int *)printfNVector { return printfNVector; }
- (int *)printfTypeVector { return printfTypeVector; }

- renderMaps:(WW3DCamera *)camera  startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime usingStream:(NXStream *)ns
{
  return self;
}

- renderMaps:(WW3DCamera *)camera usingStream:(NXStream *)ns
{
  RtFloat  shutterOpenTime = [camera shutterOpenTime],
           shutterCloseTime = [camera shutterCloseTime];


  return [self renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime usingStream:(NXStream *)ns];
}

- renderMaps:(WW3DCamera *)camera  startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  return self;
}

- renderMaps:(WW3DCamera *)camera
{
  RtFloat  shutterOpenTime = [camera shutterOpenTime],
           shutterCloseTime = [camera shutterCloseTime];


  return [self renderMaps:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
}

- renderSelfAsBox:(WW3DCamera *)camera  startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  if ([self hasBoundingBox])
  {  // if I have a bounding box, don't render myself
  }
  else
  {  // otherwise, render myself like normal
     [self renderSelf:camera  startingAt:shutterOpenTime endingAt:shutterCloseTime];
  }

  // the idea here is that if it has a boundingBox, we don't want to
  // render it (since it has some real geometry, and the whole point of
  // this routine is to avoid drawing geometry).  If, on the other hand, it
  // doesn't have a bounding box, it (at most) just transforms the current
  // transformation matrix.

  return self;
}


- renderSelf:(WW3DCamera *)camera  startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  char comment[256];

  sprintf(comment, "RIBCommand subclass %s called", [self name]);
  RiArchiveRecord("comment", comment);
  return self;
}

- renderSelf:(WW3DCamera *)camera
{
  RtFloat  shutterOpenTime = [camera shutterOpenTime],
           shutterCloseTime = [camera shutterCloseTime];


  return [self renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
}

- preRenderSelf:(WW3DCamera *)camera  startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  RiArchiveRecord("comment", "RIBCommand called"); 
  return self;
}

- preRenderSelf:(WW3DCamera *)camera
{
  RtFloat  shutterOpenTime = [camera shutterOpenTime],
           shutterCloseTime = [camera shutterCloseTime];


  return [self renderSelf:camera startingAt:shutterOpenTime endingAt:shutterCloseTime];
}

- transformCTM:(WW3DAttributeState *)attributeState startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime { return self; }

- (BOOL)isMotionBlurrable { return NO; }

- (BOOL)isCompoundCommand { return NO; }

- (BOOL)isLerpable { return NO; }

- (const char *)getInspectorClassName { return "RIBCommandIBInspector"; }

// WavesWorld archiving:
// writeEve:(NXStream *)stream
// writeScene:(NXStream *)stream

- writeEve:(NXStream *)stream atTabLevel:(int)tab
{
   int  i;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "#RIBCommand %s called;", [self name]); 
   return self;
}

- writeScene:(NXStream *)stream atTabLevel:(int)tab
{
   return [self writeEve:stream atTabLevel:tab];
}


- write3DTextScene:(NXStream *)stream atTabLevel:(int)tab index:(int)index time:(float)time until:(float)lastTime
{
   int  i;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }

   NXPrintf(stream, "set __text__(color%s) {1 1 1}\n", [[self class] name]);
   NXPrintf(stream, "startShape %s; EveCmd {Color $__text__(color%s)}; ", [[self class] name], [[self class] name]);
   // need tab
   // need index (position in current list)
   NXPrintf(stream, 
	    "EveCmd {Translate [expr { %d * $__text__(tabLength)}] [expr {$__text__(spacingFactor) * %d * $__text__(spacing) * $__text__(fontSize)}] 0 };\n", 
            tab, index); 
   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "  EveCmd {WW3DText $__text__(fontName) $__text__(fontSize) {");
   [self writeEve:stream atTabLevel:tab];
   NXPrintf(stream, "} left;}\n");
  NXPrintf(stream, "endShape;\n");

  return self;
}


// this is a routine for subclasses to call when archiving themselves to an Inventor file...
- writeParameterListForInventor:(NXStream *)stream atTabLevel:(int)tab
{
  int  i, j, k;

  
   if (!n)
   {  return nil;
   }

   // so what's the best way to do this?  basically, since Inventor is
   // pretty restricted in what it can support, we need to examine each
   // token, see if it's one that Inventor supports, and if so, spit it out.
   
   // so what does Inventor support?
   // basically, we want to be able to map the following:
   // "P" -> Coordinate3
   // "Pw" -> Coordinate4
   // "N" -> Normal
   // "st" -> TextureCoordinate2
   // "s" "t" -> need to scan for both and generate TextureCoordinate2

   for (i = 0; i < n; i++)
   {  
      if (!strcmp("P", tokens[i]))
      {  
         // I can't think of any reason that there wouldn't at least
         // two points, which the following assumes, but if there's 
         // not, I should catch it and do the right thing, sigh...
         if (printfNVector[i] < 6)
         {  for (k = 0; k < tab; k++) {  NXPrintf(stream, "\t"); }
            NXPrintf(stream, "# less than 2 points - unable to convert RenderMan P token to Inventor Coordinate3 node!\n");
            break;
         }

         j = 0;

         // write node name and first point
         for (k = 0; k < tab; k++) {  NXPrintf(stream, "\t"); }
         NXPrintf(stream, "Coordinate3 {\n");
	 for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
         NXPrintf(stream, "point [%f ", *((RtFloat *)(parms[i]) + j)); j++;
         NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j)); j++;
         NXPrintf(stream, "%f,\n", *((RtFloat *)(parms[i]) + j)); j++;

         // loop over the points, writing them out
         while (j < (printfNVector[i] - 3))
	 {  for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
	    NXPrintf(stream, "       %f ", *((RtFloat *)(parms[i]) + j)); j++;
	    NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j)); j++;
	    NXPrintf(stream, "%f,\n", *((RtFloat *)(parms[i]) + j)); j++;
 	 }

         // finish up with the last one
	 for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
	 NXPrintf(stream, "       %f ", *((RtFloat *)(parms[i]) + j)); j++;
	 NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j)); j++;
	 NXPrintf(stream, "%f]\n", *((RtFloat *)(parms[i]) + j)); j++;

         // okay, cap it off and move on
         for (k = 0; k < tab; k++) {  NXPrintf(stream, "\t"); }
	 NXPrintf(stream, "}\n", *((RtFloat *)(parms[i]) + j)); j++;
	 break;
      }

      if (!strcmp("Pw", tokens[i]))
      {  
         // I can't think of any reason that there wouldn't at least
         // two points, which the following assumes, but if there's 
         // not, I should catch it and do the right thing, sigh...
         if (printfNVector[i] < 8)
         {  for (k = 0; k < tab; k++) {  NXPrintf(stream, "\t"); }
            NXPrintf(stream, "# less than 2 points - unable to convert RenderMan Pw token to Inventor Coordinate4 node!\n");
            break;
         }

         j = 0;

         // write node name and first point
         for (k = 0; k < tab; k++) {  NXPrintf(stream, "\t"); }
         NXPrintf(stream, "Coordinate4 {\n");
	 for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
         NXPrintf(stream, "point [%f ", *((RtFloat *)(parms[i]) + j)); j++;
         NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j)); j++;
         NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j)); j++;
         NXPrintf(stream, "%f,\n", *((RtFloat *)(parms[i]) + j)); j++;

         // loop over the points, writing them out
         while (j < (printfNVector[i] - 4))
	 {  for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
	    NXPrintf(stream, "       %f ", *((RtFloat *)(parms[i]) + j)); j++;
	    NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j)); j++;
	    NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j)); j++;
	    NXPrintf(stream, "%f,\n", *((RtFloat *)(parms[i]) + j)); j++;
 	 }

         // finish up with the last one
	 for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
	 NXPrintf(stream, "       %f ", *((RtFloat *)(parms[i]) + j)); j++;
	 NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j)); j++;
	 NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j)); j++;
	 NXPrintf(stream, "%f]\n", *((RtFloat *)(parms[i]) + j)); j++;

         // okay, cap it off and move on
         for (k = 0; k < tab; k++) {  NXPrintf(stream, "\t"); }
	 NXPrintf(stream, "}\n", *((RtFloat *)(parms[i]) + j)); j++;
	 break;
     }
   }
   return self;
}

- writeInventorAtTime:(float)currentTime to:(NXStream *)stream atTabLevel:(int)tab
{
   int  i;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "# RIBCommand %s called;", [[self class] name]); 
   return self;

}


// this is a routine for subclasses to call when archiving themselves to an eve file or a scene...
- writeParameterList:(NXStream *)stream
{
  int  i, j;

  
   if (!n)
   {  NXPrintf(stream, ";");
      return nil;
   }
   for (i = 0; i < n; i++)
   {  NXPrintf(stream, "{%s} ", tokens[i]);
      switch (printfTypeVector[i])
      {  case WW_FLOAT:   NXPrintf(stream, "{ ");
                          for (j = 0; j < printfNVector[i]; j++)
	                  {  NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j));
			  }
	                  NXPrintf(stream, "} ");  
                          break;
         case WW_COLOR:   NXPrintf(stream, "{ ");
                          for (j = 0; j < printfNVector[i]; j++)
 	                  {  NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j));
			  }
	                  NXPrintf(stream, "} ");  
                          break;
         case WW_POINT:   NXPrintf(stream, "{ ");
                          for (j = 0; j < printfNVector[i]; j++)
 	                  {  NXPrintf(stream, "%f ", *((RtFloat *)(parms[i]) + j));
			  }
	                  NXPrintf(stream, "} ");  
                          break;
         case WW_INT:     NXPrintf(stream, "{ ");
                          for (j = 0; j < printfNVector[i]; j++)
 	                  {  NXPrintf(stream, "%d ", *((int *)(parms[i]) + j));
			  }
	                  NXPrintf(stream, "} ");  
                          break;
         case WW_STRING:  for (j = 0; j < printfNVector[i]; j++)
 	                  {  NXPrintf(stream, "{%s} ", *((char **)(parms[i]) + j));
			  }
                          break;
      }	  
   }
   NXPrintf(stream, ";");
   return self;
}



// NeXTSTEP archiving:

#define typeVector "i"
#define typeValues &n

- read:(NXTypedStream*)stream 
{
  int version, i, arrayLength;


  [super read:stream];

  version = NXTypedStreamClassVersion(stream,"RIBCommand");
  if (version == 0) NXReadTypes(stream,"i",&version), version=1;
  if (version == 1)
  {  NXReadArray(stream, "f", 6, boundingBox);
     NXReadTypes(stream, typeVector, typeValues);
     myShape = NXReadObject(stream);
NX_DURING
     if (n)
     {  if ([self zone])
        {  tokens = (char **)NXZoneMalloc([self zone], sizeof(char *) * n);
           archiveVector = (char **)NXZoneMalloc([self zone], sizeof(char *) * n);
           parms = (RtPointer *)NXZoneMalloc([self zone], sizeof(RtPointer) * n);
        }
        else
        {  tokens = (char **)malloc(sizeof(char *) * n);
           archiveVector = (char **)malloc(sizeof(char *) * n);
           parms = (RtPointer *)malloc(sizeof(RtPointer) * n);
        }
        for (i = 0; i < n; i++)
        {  //NXLogError("RIBCommand: attempting to read token[%d] and archiveVector[%d] of %d...\n", i, i, n);
           NXReadTypes(stream, "**", &(tokens[i]), &(archiveVector[i]));
           //NXLogError("RIBCommand: read: tokens[%d] == <%s> archiveVector[%d] == <%s>\n", 
           //           i, tokens[i], i, archiveVector[i]);
           //NXLogError("RIBCommand: got 'em!\n");
           //NXLogError("RIBCommand: attempting to read parm[%d] using archiveVector <%s>...\n", i, archiveVector[i]);

           // archiveVector is of the form [%df] or [%di]
           // therefore, we can check the second to last character, and key off that
           if (*(archiveVector[i] + strlen(archiveVector[i]) - 2) == 'f')
           {  //  it's a float
              sscanf(archiveVector[i], "[%df]", &arrayLength);
              if ([self zone])
              {  parms[i] = (float *)NXZoneMalloc([self zone], sizeof(float) * arrayLength);
              }
              else
              {  parms[i] = (float *)malloc(sizeof(float) * arrayLength);
              }
              if (!parms[i]) 
              {  NXLogError("Yikes! Unable to allocate %d bytes in read: for %s\n", 
                            (sizeof(float) * arrayLength), [[self class] name]);
              }
              else
              {  NXReadArray(stream, "f", arrayLength, parms[i]);
              }
           }
           else
           {  if (*(archiveVector[i] + strlen(archiveVector[i]) - 2) == 'i')
              {  //  it's an integer
                 sscanf(archiveVector[i], "[%di]", &arrayLength);
                 if ([self zone])
                 {  parms[i] = (int *)NXZoneMalloc([self zone], sizeof(int) * arrayLength);
 		 }
                 else
                 {  parms[i] = (int *)malloc(sizeof(int) * arrayLength);
 		 }
                 if (!parms[i]) 
                 {  NXLogError("Yikes! Unable to allocate %d bytes in read: for %s\n", 
                               (sizeof(int) * arrayLength), [[self class] name]);
                 }
                 else
                 {  NXReadArray(stream, "i", arrayLength, parms[i]);
                 }
 	      }
              else
              {  NXLogError("unable to decipher archiveVector[%d] == %s - didn't read the corresponding data\n", i, archiveVector[i]);
              }
           }
           //NXLogError("RIBCommand: got it!\n");
        }
     } 
NX_HANDLER
   NXLogError("in read:'ing the parameter list for RIBCommand, the subclass %s, exception [%d] raised where i == %d (n == %d).\n", 
                 [[self class] name], NXLocalHandler.code, i, n);
   return nil;
NX_ENDHANDLER
   }

  return self; 
}

- write:(NXTypedStream*)stream 
{
  int  i, arrayLength;

    
  [super write:stream];

  NXWriteArray(stream, "f", 6, boundingBox);
  NXWriteTypes(stream,typeVector, typeValues);
  NXWriteObjectReference(stream, myShape);
NX_DURING
  for (i = 0; i < n; i++)
  {  //NXLogError("RIBCommand: write: tokens[%d] == <%s> archiveVector[%d] == <%s>\n", 
     //           i, tokens[i], i, archiveVector[i]);
     NXWriteTypes(stream, "**", &(tokens[i]), &(archiveVector[i]));

     // archiveVector is of the form [%df] or [%di]
     // therefore, we can check the second to last character, and key off that
     if (*(archiveVector[i] + strlen(archiveVector[i]) - 2) == 'f')
     {  //  it's a float
        sscanf(archiveVector[i], "[%df]", &arrayLength);
        NXWriteArray(stream, "f", arrayLength, parms[i]);
     }
     else
     {  if (*(archiveVector[i] + strlen(archiveVector[i]) - 2) == 'i')
        {  //  it's an integer
           sscanf(archiveVector[i], "[%di]", &arrayLength);
           NXWriteArray(stream, "i", arrayLength, parms[i]);
        }
        else
        {  NXLogError("unable to decipher archiveVector[%d] == %s - didn't write the corresponding data\n", i, archiveVector[i]);
        }
     }
  }
NX_HANDLER
   NXLogError("in write:'ing the parameter list for RIBCommand, the subclass %s, exception [%d] raised where i == %d (n == %d).\n", 
                 [[self class] name], NXLocalHandler.code, i, n);
   return nil;
NX_ENDHANDLER

  return self;
}


// boy, this is dumb... This is to get around the stupid warnings from the compiler - ask wave for details
- class { return [super class]; }

@end
